package cn.tedu.charging.device.dao.repository.impl;

import cn.tedu.charging.device.dao.repository.StationCacheRepository;
import cn.tedu.charging.device.pojo.StationPO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.BoundGeoOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Repository;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 分开存
 * 位置信息 存储到 GEO
 *
 * 基本信息 存储到  基本信息 Hash 可以复用  业务上 附近充电站接口可以使用 查询场站详情接口也可以使用
 *
 * 一个站 存储到 一个Hash 不共享 方便扩展
 *
 */
@Slf4j
@Repository("GEOANDNoShareHash")
public class StationCacheGEOANDNoShareHashRepositoryImpl implements StationCacheRepository {

    @Autowired
    private RedisTemplate redisTemplate;


    @Override
    public void save(List<StationPO> stationPOS) {

        //key 站点id value 是位置 站点id和位置的映射
        Map<Integer,Point> map = new HashMap<>();

        for (StationPO stationPO : stationPOS) {
            //获取经度
            BigDecimal stationLng = stationPO.getStationLng();
            //获取维度
            BigDecimal stationLat = stationPO.getStationLat();
            //把BigDecimal转换为String 通过方法toEngineeringString
            String engineeringString = stationLng.toEngineeringString();
            //把String转换为Double
            Double stationLngDouble = Double.valueOf(engineeringString);
            //通过经纬度 构建RedisGEO 中的点 Point
            Point point = new Point(stationLngDouble,
                    Double.parseDouble(stationLat.toEngineeringString()));
            //站点id和位置的映射
            map.put(stationPO.getId(),point);

            //站点id和基本信息 stationPO 的映射
            //Map<String,Map<Key,Value>>
            //外面Map 的 key String 大key
            //里面Map<Key,Value> 小key hashkey

            //方案A ok  每个站分别存储在一个 key
            //大key 站点id
            //小key ?  属性名称 stationName
            //value ? 属性名称 对应 具体值 xx充电站

            //Map<String,Map<String,Object>> bigMap = new HashMap<>();
            //stationPO 按照字段进行拆分
            //Map<String,Object> smallMap = new HashMap<>();
            //smallMap.put("stationName",stationPO.getStationName());
            //smallMap.put("stationLng",stationPO.getStationLng());
            //bigMap.put("station_id_5",smallMap);

            //把基本信息存储到 hash 结构 一个站 一个 hash
            //定义 一个站对应的大key
            String bigKey = "station_info_hash_no_share_" + stationPO.getId();
            //定义里面的map 小map
            Map<String,Object> smallMap = new HashMap<>();
            //小key stationPO的属性名称
            String smallKey = "stationName";
            //value stationPO的属性名称对应的具体值
            String stationName = stationPO.getStationName();
            //小key 对应的 value 存储到 小map
            smallMap.put(smallKey,stationName);
            //定义操作hash 的 operations
            HashOperations hashOperations = redisTemplate.opsForHash();
            //大key 和 小map 存储到Redis
            hashOperations.putAll(bigKey,smallMap); //批量自己了解

        }
        //定义操作geo的 operations 同时绑定key  stations
        BoundGeoOperations stations = redisTemplate.boundGeoOps("stations_geo");
        //批量保存 位置信息 point  和 场站信息 stationPO 到Redis-GEO
        stations.add(map);

    }

    @Override
    public List<StationPO> search(Double longitude, Double latitude, Double distance) {
        //定义操作geo的 operations 同时绑定key  stations
        BoundGeoOperations stations = redisTemplate.boundGeoOps("stations_geo");

        Circle circle  = getCircle(longitude,latitude,distance);
        log.debug("通过RedisGEO Circle:{} 查询附近充电站",circle);
        GeoResults<RedisGeoCommands.GeoLocation<Integer>> radius = stations.radius(circle);
        log.debug("通过RedisGEO Circle:{} 查询附近充电站:{}",circle,radius);

        List<StationPO> stationPOS = new ArrayList<>();

        for (GeoResult<RedisGeoCommands.GeoLocation<Integer>> geoLocationGeoResult : radius) {
            //GeoResults 里的具体内容 用 content表示
            RedisGeoCommands.GeoLocation<Integer> content =
                    geoLocationGeoResult.getContent();
            //name 表示 GEOADD 命令里的 参数  member
            Integer stationId = content.getName();
            //通过 id 去 分开存的 场站信息获取具体场站的数据

            //get
            //定义操作 Hash 的 operations
            HashOperations hashOperations = redisTemplate.opsForHash();
            // 一个站对应的大key
            String bigKey = "station_info_hash_no_share_" + stationId;
            //entries 可以通过大key 获取到 小map
            Map smallMap = hashOperations.entries(bigKey);
            // 小key stationPO的属性名称
            String smallKey = "stationName";
            // 属性名称对应的具体值
            Object name = smallMap.get(smallKey);
            //把对象转换为 String
            String stationName = String.valueOf(name);
            StationPO stationPO = new StationPO();
            stationPO.setStationName(stationName);
            stationPOS.add(stationPO);
        }

        return stationPOS;
    }

    /**
     * 通过点和半径构建一个圆
     * @param longitude
     * @param latitude
     * @param distance
     * @return
     */
    private Circle getCircle(Double longitude, Double latitude, Double distance) {
        //通过入参的经纬度 构建一个点
        Point point = new Point(longitude, latitude);
        //通过入参的距离 设置半径 同时设置单位为 KILOMETERS
        Distance geoDistance = new Distance(distance, Metrics.KILOMETERS);
        //通过点和半径 画圆
        Circle circle = new Circle(point, geoDistance);
        return circle;
    }
}
